#include "field.h"

#include <iostream>
#include <fstream>
#include <exception>
#include <algorithm>
#include "xmlhelpers.h"

Field::Field () {
}

Field::~Field () {
}

Texel & Field::operator() (int x, int y) {
	int nx, ny;
	ny = ((y % height) >= 0) ? y % height : y % height + height;
	nx = ((x % width) >= 0) ? x % width : x % width + width;
	return at(ny*width + nx);
}

void Field::Load (const std::string & file, ObjTable & objTbl) {
	//Prepearing file
	std::ifstream inputXML(file.c_str());
	std::string line, text;
	while (getline(inputXML, line)) {
		text += line;
	}
	xmlBuf.assign(text.begin(), text.end());
	xmlBuf.push_back('\0');
	xml.parse<0> (&xmlBuf[0]);

	//Now capturing the map itself
	rapidxml::xml_node<> * node = XMLGetNode(xml, "field");
	XMLGetValue(width, node, "width");
	XMLGetValue(height, node, "height");

	std::string map;
	XMLGetValue(map, node);

	//Make up field
	const char * fld = map.c_str();
	resize(width * height);
	try {
		for (int j = 0, i = 0; j < width*height; i++) {
			if (fld[i]) {
				if (fld[i] != ' ' && fld[i] != '\n' && fld[i] != '\t') {
					(*this)[j++] = objTbl[fld[i]];
				}
			} else {
				throw Error("Map is too short for this width and height");
			}
		}
	} catch (std::exception & err) {
		throw Error("Error loading map");
	}
}

void Field::Draw (SDL_Surface * screen) {
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			(*this)(x, y).Draw(screen, x, y);
		}
	}
}


void Field::HandleTick (unsigned time) {
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			Texel & tmp = (*this)(x, y);
			if (tmp.IsTickable()) {
				tmp.HandleTick(time);
				if(tmp.ReadyToExplode()) {
					Explode(x, y, tmp.bombDamage, tmp.bombRadius);
				}
			}
		}
	}
}

void Field::Explode (int bx, int by, int damage, int radius) {
	for (int y = std::max(by - radius, 0); y < std::min(by + radius, height); y++) {
		for (int x = std::max(bx - radius, 0); x < std::min(bx + radius, width); x++) {
			if ((x-bx)*(x-bx)+(y-by)*(y-by) <= radius*radius) {
				(*this)(x, y).Explode(damage);
			}
		}
	}
	(*this)(bx, by).Demolish();
}
